package org.javaexpert.spaces.masterworker.masterexample;

import net.jini.core.entry.UnusableEntryException;
import net.jini.core.lease.LeaseDeniedException;
import net.jini.core.transaction.Transaction;
import net.jini.core.transaction.TransactionException;
import net.jini.core.transaction.TransactionFactory;
import net.jini.core.transaction.server.TransactionManager;
import net.jini.space.JavaSpace;
import org.javaexpert.spaces.masterworker.TaskBag;
import org.javaexpert.spaces.util.SpaceUtils;
import org.javaexpert.spaces.util.TransactionTemplate;

import java.io.Serializable;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;


public class Master {
    private JavaSpace space;
    private TransactionManager mgr;
    private static final int ADD_COUNT = 100;
    private TaskBag taskBag;
    private List<Future<Integer>> futures;


    public static void main(String[] args) throws Exception {
        Master master = new Master();
        master.startComputing();
    }


    private void startComputing() throws Exception {
        space = SpaceUtils.findSpace("mySpace", "localhost");
        mgr = SpaceUtils.findTransactionManager("localhost");
        taskBag = new TaskBag("bag1", space, new TransactionTemplate(mgr));

        futures = generateTasks();
        collectResults(futures);
    }


    private List<Future<Integer>> generateTasks() throws RemoteException, UnusableEntryException, TransactionException, InterruptedException, LeaseDeniedException {
        List<Future<Integer>> results = new ArrayList<Future<Integer>>();
        for (int i = 0; i < ADD_COUNT; i++) {
            try {
                results.add(taskBag.submit(new AddCallable(i)));
                results.add(taskBag.submit(new MultCallable(i)));
                System.out.print(".");

            } catch (Exception e) {
                System.out.println("i1=" + i);
                e.printStackTrace();
            }
        }
        return results;
    }


    protected void collectResults(List<Future<Integer>> futures) throws ExecutionException, InterruptedException {
        int i = 0;
        for (Future<Integer> f : futures) {
            System.out.printf("res_%d:%d\n", (i++), getResult(f));
        }
    }

    private Integer getResult(Future<Integer> f) throws InterruptedException, ExecutionException {
        try {
            return f.get();
        } catch (ExecutionException e) {
            return -1;
        }
    }


    public Transaction getTransaction(long leaseTime) {
        try {
            Transaction.Created created =
                    TransactionFactory.create(mgr, leaseTime);
            return created.transaction;
        } catch (RemoteException e) {
            e.printStackTrace();
            return null;
        } catch (LeaseDeniedException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static class AddCallable implements Callable<Integer>, Serializable {
        public Integer i1;
        private Random random = new Random(System.currentTimeMillis());

        public AddCallable(int i1) {
            this.i1 = i1;
        }

        public Integer call() throws Exception {
            randomSleep();
            switch (random.nextInt(500)) {
                case 4:
                    throw new RuntimeException("-----exception----");
                case 5:
                    throw new Error("-----ERROR------");
                case 6:
                    System.exit(-1);
                case 7:
                    return null;
            }


            return i1 + i1;
        }

        private void randomSleep() {
            try {
                Thread.sleep(random.nextInt(10) * 100);
            } catch (InterruptedException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }
        }
    }

    public static class MultCallable implements Callable<Integer>, Serializable {
        public Integer i1;


        public MultCallable(int i1) {
            this.i1 = i1;
        }

        public Integer call() throws Exception {
            return i1 * i1;
        }
    }
}

